/*
 *
 *   Copyright 2007-2008 University Of Southern California
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing,
 *   software distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
package org.griphyn.vdl.dax;


import org.griphyn.vdl.classes.LFN;

import java.io.IOException;
import java.io.Writer;

import java.util.Iterator;

/**
 * This class defines the DAX job in an abstract manner.
 *
 * @author Karan Vahi
 */
public class DAGJob extends Job implements Cloneable{

    /**
     * The DAX LFN.
     */
    private Filename mDAGLFN;
    
    /**
     * The DAX PFN. They are separate till the Filename object encompasses
     * a PFN object.
     */
    private String mPFN;
    
    /**
     * The site with which PFN is associated.
     */
    private String mSite;

    /**
     * Overloaded constructor.
     * 
     * @param id       the id to be assigned to a job.
     * @param daxLFN   the lfn to be assigned to the DAX
     */
    public DAGJob( String id, String daxLFN ){
        this.setID( id );
        this.mDAGLFN = new Filename( daxLFN, LFN.INPUT );
        this.mDAGLFN.setRegister( false );
        this.mDAGLFN.setTransfer( LFN.XFER_MANDATORY );
    }

    /**
     * Sets the PFN for the DAX.
     *
     * @param pfn
     * @param site
     */
    public void setDAXPFN( String pfn, String site ){
        this.mPFN  = pfn;
        this.mSite = site;
    }


    /**
     * Dump the state of the current element as XML output. This function
     * traverses all sibling classes as necessary, and converts the data
     * into pretty-printed XML output. The stream interface should be able
     * to handle large output efficiently.
     *
     * @param stream is a stream opened and ready for writing. This can also
     * be a string stream for efficient output.
     * @param indent is a <code>String</code> of spaces used for pretty
     * printing. The initial amount of spaces should be an empty string.
     * The parameter is used internally for the recursive traversal.
     * @param namespace is the XML schema namespace prefix. If neither
     * empty nor null, each element will be prefixed with this prefix,
     * and the root element will map the XML namespace.
     */
    public void toXML( Writer stream, String indent, String namespace )
        throws IOException{
        String newline = System.getProperty( "line.separator", "\r\n" );
        String tag = ( namespace != null && namespace.length() > 0 ) ?
         namespace + ":dag" : "dag";
        String tag2 = ( namespace != null && namespace.length() > 0 ) ?
         namespace + ":argument" : "argument";

        // open tag
        if ( indent != null && indent.length() > 0 ) stream.write( indent );
        stream.write( '<' );
        stream.write( tag );


        writeAttribute( stream, " file=\"", this.mDAGLFN.getFilename() );

        writeAttribute( stream, " id=\"", this.getID() );

        // misc. attributes like the search tree depth
        if ( this.getLevel() != -1 )
        writeAttribute( stream, " level=\"", Integer.toString( this.getLevel()) );



        // open tag: finish opening tag
        stream.write( '>' );
        if ( indent != null ) stream.write( newline );

        // concat all command line fragments into one big string.
        String newindent = indent==null ? null : indent + "  ";
        if ( this.getArgumentCount() > 0 ) {
            if ( newindent != null ) stream.write( newindent );

                stream.write( '<' );
                stream.write( tag2 );
                stream.write( '>' );

                for ( Iterator i = this.getArgumentList().iterator(); i.hasNext(); ) {
                    // casting will print a mixed content string or Filename element
                    ((Leaf) i.next()).shortXML( stream, "", namespace, 0x00 );
                }
                stream.write( "</" );
                stream.write( tag2 );
                stream.write( '>' );
                if ( indent != null ) stream.write( newline );
        }

        // profiles to be dumped next
        for ( Iterator i=this.getProfileList().iterator(); i.hasNext(); ) {
            ((Profile) i.next()).toXML( stream, newindent, namespace );
        }

        // finally any bound stdio descriptor
        // FIXME: really need to dump a Filename element!
    /* not populating stdin/stdout/stderr/other filenames for now */
        /*
        if ( this.getStdin() != null )
            stream.write( formatFilename( "stdin", newindent, namespace,
				    this.getStdin(), false) );
        if ( this.m_stdout != null )
            stream.write( formatFilename( "stdout", newindent, namespace,
				    this.m_stdout, false) );
        if ( this.m_stderr != null )
            stream.write( formatFilename( "stderr", newindent, namespace,
				    this.m_stderr, false) );

        // VDL referenced Filenames to be dumped next
        for ( Iterator i=this.m_usesList.iterator(); i.hasNext(); ) {
            stream.write( formatFilename( "uses", newindent, namespace,
				    (Filename) i.next(), true) );
        }
        */

        //write out the DAX file contents
        stream.write( this.formatFilename( newindent, namespace, mDAGLFN , mPFN, mSite ) );

        // finish job
        if ( indent != null && indent.length() > 0 ) stream.write( indent );
        stream.write( "</" );
        stream.write( tag );
        stream.write( '>' );
        if ( indent != null ) stream.write( newline );

    }



    /**
     * Helper: Formats the uses tags with the nested PFN tag.
     *
     * @param indent is the indentation of the element, may be null.
     * @param namespace is an optional namespace to use in the tag.
     * @param f is an instance of a <code>Filename</code> object.
     * @param pfn   the pfn for the dax file
     * @param site  the site attribute for the PFN
     *
     * @return the XML-formatted attributes without the element tags.
     */
     private String formatFilename( String indent, String namespace,
				 Filename f, String pfn, String site ){

         String newline = System.getProperty( "line.separator", "\r\n" );
         String tag = "uses";
         StringBuffer result = new StringBuffer( 128 );
         if ( namespace != null && namespace.length() > 0 )
            tag = namespace + ":" + tag;

         if ( indent != null && indent.length() > 0 ) result.append( indent );

         result.append('<').append(tag);
         result.append(" file=\"").append(quote(f.getFilename(),true)).append('"');
         result.append(" link=\"").append(LFN.toString(f.getLink())).append('"');


         result.append(" register=\"")
                  .append(Boolean.toString( f.getRegister())).append('"');
         result.append(" transfer=\"")
                  .append(LFN.transferString( f.getTransfer() )).append('"');
         result.append(" type=\"")
                  .append(LFN.typeString( f.getType() )).append('"');

         if( f.getOptional() )
            result.append( " optional=\"" ).append( f.getOptional() ).append( "\"" );

         if ( f.getTemporary() != null )
            result.append(" temporaryHint=\"")
                    .append(quote(f.getTemporary(),true)).append('"');

         //add a nested pfn if required.
         if( pfn != null ){
             result.append( ">" ).append( newline );
             String newindent = indent==null ? null : indent + "  ";
             result.append( newindent );

             result.append( "<pfn url=\"" ).append( this.mPFN ).append( "\"" ).
                    append( " site=\"").append( this.mSite ).append(  "\"" ).
                    append( "/>" );

             result.append( newline);
             if ( indent != null && indent.length() > 0 ) result.append( indent );

             result.append("</" ).append( tag ).append( ">");

         }
         else{// add newline and done
            result.append("/>" );
         }
         
         if ( indent != null )
            result.append( newline );

         return result.toString();

     }


    /**
     * Creates and returns a copy of this object.
     *
     * @return a new instance, semi-deep copy
     */
    public Object clone(){
        DAGJob result = (DAGJob) super.clone();
        result.setDAXPFN( this.mPFN, this.mSite );
        result.mDAGLFN = (Filename)this.mDAGLFN.clone();

        return result;
    }
}
